package fr.lelouet.tools.sequentialIterators;

import java.util.Collection;
import java.util.Iterator;
import java.util.Properties;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import fr.lelouet.tools.SequentialIterator;


/**
 * Iterates over the set given in {@link #startNewLoop(Set)};<br />
 * Wait some time between each request. That time is {@link #between_loop_ms}
 * /nbrequests, where nbrequest is the number of elements to iterate over.
 * 
 * @see SequentialIterator the interface
 */
public class PeriodicUniform<T> implements SequentialIterator<T> {

	/**
	 * the key in the properties to configure the delay between two requests
	 * sends
	 */
	public static final String DELAY_MS_KEY = "periodicalrequest.ms";

	public static final long DELAY_MS_DEFAULT = 1000;

	private static final Logger logger = LoggerFactory
			.getLogger(PeriodicUniform.class);

	protected Iterator<T> internalIt;

	@Override
	public boolean hasNext() {
		return internalIt.hasNext();
	}

	private long between_loop_ms = DELAY_MS_DEFAULT;
	private long betweenProbe_ms;
	private long nextTime = 0;

	@Override
	public T next() {
		getNextTick();
		return internalIt.next();
	}

	@Override
	public void remove() {
		throw new UnsupportedOperationException();
	}

	@Override
	public void startNewLoop(Collection<T> requests) {
		int cycles = requests.size();
		if (cycles < 1) {
			betweenProbe_ms = between_loop_ms;
		} else {
			betweenProbe_ms = between_loop_ms / cycles;
		}
		internalIt = requests.iterator();
		/**
		 * if cycles==0, that means there will not be a next() because hasNext()
		 * will return 0. So, we must wait now.
		 */
		if (cycles == 0) {
			getNextTick();
		}
	}

	/** wait for the next correct time this can iterate. */
	protected void getNextTick() {
		long delay = nextTime - System.currentTimeMillis();
		if (delay > 0) {
			try {
				Thread.sleep(delay);
			} catch (InterruptedException e) {
				logger.error(e.toString());
			}
		}
		long minTime = System.currentTimeMillis() - betweenProbe_ms;
		nextTime = nextTime + betweenProbe_ms;
		nextTime = nextTime > minTime ? nextTime : minTime;
	}

	/**
	 * get the {@link #between_loop_ms time between two loops} from prop if it
	 * is specified
	 */
	@Override
	public void configure(Properties prop) {
		String val = prop.getProperty(DELAY_MS_KEY);
		if (val == null) {
			return;
		}
		try {
			long mdelay = Long.parseLong(val);
			if (mdelay > 0) {
				between_loop_ms = mdelay;
			}
		} catch (NumberFormatException ex) {
			logger.error(ex.toString());
		}
	}

}
